package com.han.yrpc.core.runtime.nio;

import com.han.yrpc.core.runtime.YrpcServer;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NioYrpcServer implements YrpcServer {

    private final int DEFAULT_PORT = 8888;
    private int port = DEFAULT_PORT;

    private final ExecutorService executor =
            Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    @Override
    public int getPort() {
        return 0;
    }

    @Override
    public void start() {
        try {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);

            String hostName = InetAddress.getLocalHost().getHostName();
            serverSocketChannel.bind(new InetSocketAddress(hostName, port));

            Selector selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            executor.execute(() -> this.handler(selector));

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void handler(Selector selector) {
        while (true) {
            try {
                selector.select(1000);
                Set<SelectionKey> keys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = keys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    if (key.isAcceptable()) {
                        ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                        SocketChannel socketChannel = channel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    }
                    if (key.isReadable()) {
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        int read = socketChannel.read(buffer);
                        if (read > 0) {
                            buffer.flip();
                            byte[] bytes = new byte[buffer.remaining()];
                            buffer.get(bytes);
                            String str = new String(bytes);
                            String res = "Hello " + str;
                            byte[] bytes1 = res.getBytes();
                            ByteBuffer buffer1 = ByteBuffer.allocate(bytes1.length);
                            buffer1.put(buffer1);
                            buffer1.flip();
                            socketChannel.write(buffer1);
                        } else if (read < 0) {
                            key.cancel();
                            socketChannel.close();
                        }
                    }
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
